#coding=utf8
from google.appengine.ext import db
from google.appengine.ext import webapp

from common import *

'''
spike task module
chuan's dog food
'''

class TaskObject(db.Model):
    "db for task"
    # in fact, task_id is necessarily
    task_id = db.StringProperty(required=False)
    tasknode_data = db.BlobProperty(required=True)
    description = db.StringProperty(required=False)
    state = db.IntegerProperty(required=True)
    owner = db.StringProperty(required=True)


# tasknode = ['name', 'duetime', 'remidertime', 'index']
# task = [taskid, tasknode_list, description, state, owner]
# state {}

taskstate_dic = {
    'normal': 1,
    'alarm': 2,
    'done': 3,
    'discard': 4
    }
# terrible code -_-#
taskstate_name_dic = {
    1: 'normal',
    2: 'alarm',
    3: 'done',
    4: 'discard'
    }

def get_task_by_id(task_id):
    return db.get(task_id)

def get_tasks_by_owner(owner):
    return db.GqlQuery("select * from TaskObject where owner = :1",
                       str(owner)).fetch(1000)

def clean_all_task(owner):
    tasklst = db.GqlQuery("select * from TaskObject where owner = :1", 
                          str(owner)).fetch(1000)
    if tasklst:
        for task in tasklst:
            task.delete()

def remove_task_by_id(task_id):
    task = get_task_by_id(task_id)
    if task:
        task.remove()
        return True
    return False

def get_all_task():
    return db.GqlQuery("select * from TaskObject").fetch(1000)


# TRY to write code as a new style:
# DON'T write a db layer to apply some wrapped function to write/read datebase
# we just call GQL directly in class
class SpikeTask(object):
    def __init__(self, owner='', task_id=-1):
        # entity in google db
        self.data_entity = None
        # must set an owner for each task
        self.owner = owner
        # key for task obj
        self.key = None

        self.class_state = False
        if task_id != -1:
            self._load_task(task_id)
            self.class_state = True
        else:
            # new task
            self._new_task()

    def get_current_tasknode(self):
        if self.tasknode_list:
            return self.tasknode_list[-1]
        return None

    def get_history_tasknode(self, index):
        for tasknode in self.tasknode_list:
            if tasknode.index == index:
                return tasknode
        return None

    # if user call change_xxx, it will call this function later
    # and it cost extra get_current_tasknode routine
    # and in fact, doesn't matter, in my opinion
    def _add_new_tasknode(self, node):
        if not self._check_tasknode(node):
            return False
        if node[:-1] == self.get_current_tasknode()[:-1]:
            return False
        # if index is not equal (currentnode.index + 1)
        if (node[-1] - 1) != self.get_current_tasknode()[-1]:
            return False

        # yes, copy is necessary
        self.tasknode_list.append(list(node))
        return True

    def set_state(self, target_status):
        if taskstate_dic.has_key(target_status):
            self.state = taskstate_dic[target_status]
        elif taskstate_name_dic.has_key(target_status):
            self.state = target_status
        else:
            return False
        return True

    def change_name(self, name):
        return self._change_tasknode(0, name)

    def change_duetime(self, duetime):
        return self._change_tasknode(1, duetime)
    
    def change_remidertime(self, remidertime):
        return self._change_tasknode(2, remidertime)

    def set_description(self, description):
        self.description = description

    # this function may not use
    def remove_name_by_index(self, index):
        pass

    def _load_task(self, task_id):
        "load task from db according to task id"
        self.data_entity = db.get(task_id)
        if self.data_entity:
            self.task_id = self.data_entity.task_id
            self.owner = self.data_entity.owner
            self.tasknode_list = str_to_val(self.data_entity.tasknode_data)
            self.state = self.data_entity.state
            self.description = self.data_entity.description

    def _new_task(self):
        self.tasknode_list = []
        self.description = ''
        self.state = taskstate_dic['normal']

    def init_task(self, tasknode):
        tasknode.append(0)
        if self._check_tasknode(tasknode):
            self.tasknode_list.append(tasknode)
            return True
        return False

    def _new_task_save(self):
        "save new task entity"
        self.data_entity = TaskObject(tasknode_data=val_to_str(self.tasknode_list),
                                      description=self.description,
                                      owner=self.owner,
                                      state=self.state)
        self.key = self.data_entity.put()
        if self.key:
            self.data_entity.task_id = self.key.__str__()
            self.task_id = self.data_entity.task_id
            if self.data_entity.put():
                return True
        return False

    def _save(self):
        "save entity for normal task"
        # Defensive programming
        if self.data_entity.task_id != self.task_id \
                or self.data_entity.owner != self.owner:
            return False
        self.data_entity.description = self.description
        self.data_entity.tasknode_data = val_to_str(self.tasknode_list)
        self.data_entity.state = self.state

        if self.data_entity.put():
            return True
        return False

    def _check_tasknode(self, node):
        if len(node) < 4:
            return False
        if type(node[0]) != unicode or type(node[1]) != int \
                or type(node[2]) != int or type(node[3]) != int:
            return False
        if node[1] < 0 or node[2] < 0:
            return False
        
        return True

    def _change_tasknode(self, index, val):
        newnode = self.get_current_tasknode()
        if not newnode:
            return False
        newnode = list(newnode)
        if index < 0 or index > 3:
            return False
        if val == newnode[index]:
            return False
        newnode[index] = val
        newnode[3] = newnode[3] + 1
        return self._add_new_tasknode(newnode)

    def save(self):
        "save task to db"
        # if task is new
        if not self.data_entity:
            return self._new_task_save()
        return self._save()

    # in fact, user can call remove_name_by_index directly
    def remove(self):
        if self.data_entity:
            if self.data_entity.remove():
                return True
        return False

    # dump task to simple val
    def dump_task(self):
        node_list = [node[:3] for node in self.tasknode_list]
        for node in node_list:
            node[0] = node[0].encode('utf8')
        return {
            'task_id' : self.task_id,
            'owner' : self.owner,
            'state' : self.state,
            'description' : self.description,
            'node' : self.tasknode_list
            }
